/**
 *
 */
package com.monkeyboy.security.config;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.social.security.SpringSocialConfigurer;

import com.monkeyboy.security.authentication.MyAuthenticationFailureHandler;
import com.monkeyboy.security.authentication.MyAuthenticationSuccessHandler;
import com.monkeyboy.security.authentication.mobile.SmsCodeSecurityConfig;
import com.monkeyboy.security.logout.MyLogOutHandler;
import com.monkeyboy.security.properties.SecurityProperties;
import com.monkeyboy.security.service.MyUserDetailsService;
import com.monkeyboy.security.session.MySessionExpireStrategy;

/**
 * @Description 总配置，把所有额配置柔和在一起
 * @author Gavin<br>
 *         2019年8月25日
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	@Autowired
	private SecurityProperties securityProperties;

	@Autowired
	private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
	@Autowired
	private MyAuthenticationFailureHandler myAuthenticationFailureHandler;
	@Autowired
	private ValidateCodeSecurityConfig validateCodeSecurityConfig;// 验证码
	@Autowired
	private SmsCodeSecurityConfig smsCodeSecurityConfig;// 导入短信验证码登录的配置
	@Autowired
	private SpringSocialConfigurer mySpringSocialConfigure;// springsocial拦截器配置
	@Autowired
	private MyLogOutHandler myLogOutHandler;
	/**
	 * ################### 实现记住我的功能
	 *
	 */
	@Autowired
	private DataSource dataSource;
	@Autowired
	private MyUserDetailsService myUserDetailsService;

	@Bean
	public PersistentTokenRepository persistentTokenRepository() {
		JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
		tokenRepository.setDataSource(dataSource);
		// tokenRepository.setCreateTableOnStartup(true);//
		// 启动时候会自动建表（如果有表就不需要设置了，否则启动会报错）
		// 查看源代码发现会自动给你创建一个表来给你存放token
		return tokenRepository;
	}

	/**
	 * ########################实现记住我的功能
	 *
	 */
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.apply(smsCodeSecurityConfig)// 依赖手机短信登录
				.and().apply(validateCodeSecurityConfig)// 设置验证码的过滤器
				.and().apply(mySpringSocialConfigure)// 添加springsocial拦截器配置
				.and().formLogin()// 表单认证的方式
				// .loginPage("/mylogin.html")// 自定义登录页面,返回的是一个页面
					.loginPage("/authentication/require")// 自定义的一个控制器路径，可控制返回页面还是ajax数据
					.loginProcessingUrl("/authentication/form")// 提交该请求时候springsecurity就会用UsernamePasswordAuthenticationFilter过滤器来处理
					.successHandler(myAuthenticationSuccessHandler)// 使用自定义的成功处理器，以便对ajax数据进行返回
					.failureHandler(myAuthenticationFailureHandler)// 使用自定义的错误处理器
				.and().rememberMe()// 记住我功能添加
					.tokenRepository(persistentTokenRepository())// 设置一个获取token的处理类
					.tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds())// 设置记住我的时间
					.userDetailsService(myUserDetailsService)// 把处理好的信息存到myUserDetailsService中再进行接下来的处理
				.and().sessionManagement()// 对session的管理,如果设置把session存入到redis中那这里作用也是生效的不影响
					.invalidSessionUrl("/session/invalid")// session失效时候跳转的路径
					.maximumSessions(1)// session只能一个用户登录，如果两个用户都登录了那么后者会挤掉前者
					.maxSessionsPreventsLogin(true)// 不加的话就是后者登录挤掉前者登录的用户，设置为true则提示用户已经登录，你不能再进行登录,返回的默认消息可以在自定以的失败处理器中处理
					.expiredSessionStrategy(new MySessionExpireStrategy())// 自定义session失效时候的控制策略
				.and().and().logout()// 使用自定义的退出登录地址
					.logoutUrl("/userLogout")//
				// .logoutSuccessUrl("/mylogin.html")//退出登录成功后跳转的路径
					.logoutSuccessHandler(myLogOutHandler)// 自定义登录成功后的处理器，注意logoutSuccessUrl和logoutSuccessHandler只能使用一个
					.deleteCookies("JSESSIONID")// 退出成功后把浏览器中的session删除
				.and().authorizeRequests()// 对下面的请求进行授权配置
					.antMatchers("/authentication/require", securityProperties.getBrowser().getLoginpage(),
						"/session/invalid", "/user/regist", "/signup.html", "/social/user")
					.permitAll()
					//.antMatchers("/user/getMe").hasRole("ADMIN")//只有拥有ROLE_ADMIN这个角色才可以访问/user这个请求
					.antMatchers(HttpMethod.GET,"/user/*").hasRole("ADMIN")
				.anyRequest()// 任何请求
				.authenticated()// 需要授权
				.and().csrf().disable()// 禁用csrf
		;

	}

//	@Bean
//	@Override
//	public UserDetailsService userDetailsService(){
//		return new MyUserDetailsService();
//	}
//	@Bean
//	public BCryptPasswordEncoder passwordEncoder(){
//		return new BCryptPasswordEncoder();
//	}
//	@Override
//	protected void configure(AuthenticationManagerBuilder auth)throws Exception{
//		auth.userDetailsService(userDetailsService());
//	}
//	@Override
//	public void configure(WebSecurity web)throws Exception{
//		web.ignoring().antMatchers("/oauth/check_token");
//	}
}
